<?php
namespace App\Services\Payment\Order;

use App\Jobs\OrderCallbackJob;
use App\Models\CacheDb\Order\RealTimePayCache;
use App\Models\ConstCode;
use App\Models\ConstCodeArray;
use App\Models\MysqlDb\PaymentOrderModel;
use App\Models\StatusCode;
use App\Services\BaseService;
use App\Services\CommonReturn;
use App\Services\CommonServices;
use App\Services\Payment\ConfigService;
use Illuminate\Support\Facades\Log;
use Yansongda\Pay\Pay;

class PayServices extends BaseService
{
    /**
     * @var
     */
    public $config;
    /**
     * @var
     */
    public $company;
    /**
     * @var
     */
    public $params;
    /**
     * @var
     */
    public $app_id;

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->model = new PaymentOrderModel();
    }


    /**
     * 创建第三方支付订单
     * @param $company
     * @param $id
     * @return array|array[]|string[]
     */
    public function createThirdPayOrder($company,$id){
        $order = $this->model->where('id',$id)->first();
        //创建支付
        $params = json_decode($order->params,true);
        $method = $order['method'];
        $real_time_status = ConstCode::REAL_TIME_PAY_WAIT;//未支付

        $payment = ConstCodeArray::getPayMent($order['payment_method']);
        $config = (new ConfigService)->getParamsConfig($company,$payment);
        $this->log("支付config: ".json_encode($config));

        $service = Pay::$payment($config);
        if($order['payment_method'] == ConstCode::PAYMENT_ALIPAY){
            $data = [
                'out_trade_no' => $order['out_trade_no'],
                'total_amount' => $order['pay_amount'],
                'subject' => $order['description'],
            ];
            if($order['method'] == 'pos'){
                $data['auth_code'] = $params['auth_code'];
            }
        }elseif($order['payment_method'] == ConstCode::PAYMENT_WECHAT){
            $data = [
                'out_trade_no' => $order['out_trade_no'],
                'description' => $order['description'],
                'amount' => [
                    'total' => $order['pay_amount'] * 100,
                    'currency' => 'CNY',
                ],
            ];
            $wechatConfig = $config['wechat']['default'];
            if(in_array($order['method'],['mini','mp'])){
                if(empty($order['openid'])){
                    CommonReturn::fail(StatusCode::COMMON_MSG,'openid not found');
                }
                if (Pay::MODE_SERVICE === ($wechatConfig['mode'] ?? Pay::MODE_NORMAL)) {
                    $data['payer']['sp_openid'] = $order['openid'];//服务商
                }else{
                    $data['payer']['openid'] = $order['openid'];//子商户
                }
            }elseif($order['method'] == 'pos'){
                $data['payer']['auth_code'] = $params['auth_code'];
                if(!empty($wechatConfig['store_id'])){
                    $data['scene_info']['store_info']['id'] = $wechatConfig['store_id'];
                }else{
                    $data['scene_info']['store_info']['id'] = '1';//系统微信支付默认店铺id
                }
            }
        }else{
            CommonReturn::fail(StatusCode::COMMON_PARAM_INVALID,"payment_method invalid");
        }

        try{
            //创建第三方订单并支付
            $result = $service->$method($data);
            if($result instanceof \GuzzleHttp\Psr7\Response){
                $result = [
                    'data'=>$result->getBody()->getContents(),
                ];
            }
            if($result instanceof \Yansongda\Supports\Collection){
                $result = $result->toArray();

                if($method == 'pos'){
                    $this->log('scanPay pos result:'.json_encode($result));
                    /**
                     * 支付成功
                     */
                    //修改支付状态
                    if($order['payment_method'] == ConstCode::PAYMENT_WECHAT){
                        if(!empty($result['trade_state']) && $result['trade_state'] == 'USERPAYING'){
                            $real_time_status = ConstCode::REAL_TIME_PAY_USER_PAYING;//微信需要输密码
                            RealTimePayCache::setStatus($order['out_trade_no'],$real_time_status);

                            $result['real_time_status'] = $real_time_status;
                            $this->log('result : real_time_status:'.$real_time_status);
                            return $result;
                        }if($result['trade_state'] != 'SUCCESS'){
                            //支付失败状态码
                            CommonReturn::fail(StatusCode::COMMON_MSG,$result['trade_state_desc']);
                        }

                        //微信
                        $total_fee = bcdiv($result['amount']['total'], 100,2);
                        $transaction_id = $result['transaction_id'];
                        $success_time = CommonServices::dateTimeSetZone($result['success_time'])->getTimestamp();
                    }elseif($order['payment_method'] == ConstCode::PAYMENT_ALIPAY){
                        if(!empty($result['sub_code'])){
                            switch ($result['sub_code']){
                                case 'ACQ.PAYMENT_AUTH_CODE_INVALID':$message = "付款码失效，请用户刷新付款码后重新扫码";break;
                                case 'ACQ.BUYER_BALANCE_NOT_ENOUGH':$message = "买家余额不足";break;
                                case 'ACQ.BUYER_BANKCARD_BALANCE_NOT_ENOUGH':$message = "用户银行卡余额不足";break;
                                case 'ACQ.TRADE_HAS_SUCCESS':$message = "用户已经支付，请勿重复收款。";break;
                                default: $message = $result['sub_msg'];
                            }
                            CommonReturn::fail(StatusCode::COMMON_MSG,$message);
                        }
                        if($result['code'] == 10003){//10000 支付成功，10003 用户需要输入密码
                            $real_time_status = ConstCode::REAL_TIME_PAY_USER_PAYING;//支付宝需要输密码
                            RealTimePayCache::setStatus($order['out_trade_no'],$real_time_status);

                            $result['real_time_status'] = $real_time_status;
                            $this->log('result : real_time_status:'.$real_time_status);
                            return $result;
                        }elseif($result['code'] != 10000){
                            //支付失败状态码
                            CommonReturn::fail($result['code'],$result['sub_msg']);
                        }

                        //支付宝
                        $total_fee = $result['total_amount'];
                        $transaction_id = $result['trade_no'];
                        $success_time = NOW_TIME;
                    }else{
                        CommonReturn::fail(StatusCode::COMMON_PARAM_INVALID,"pos payment_method invalid");
                    }
                    $this->log('pos: '.json_encode($result));

                    $pay = $this->isPay($this->app_id,$result['out_trade_no'],$total_fee,$success_time,$transaction_id);
                    if($pay == false){
                        $real_time_status = ConstCode::REAL_TIME_PAY_FAIL;
                    }else{
                        $real_time_status = ConstCode::REAL_TIME_PAY_SUCCESS;

                        //回调订单状态
                        OrderCallbackJob::dispatch($pay);
                    }
                    RealTimePayCache::setStatus($order['out_trade_no'],$real_time_status);
                }
            }elseif($result instanceof \Symfony\Component\HttpFoundation\Response){
                //支付宝/wechat app支付时返回Response类
                $content = "";
                foreach((array)$result as $key=>$value){
                    if(trim(str_replace('*','',$key)) == "content"){
                        $content = $value;
                    }
                }

                $data = json_decode($content,true);
                if(is_array($data)){
                    $result = [
                        'data'=>$data,
                    ];
                }else{
                    $result = [
                        'data'=>$content,
                    ];
                }
                $this->log('支付宝/wechat app支付时返回Response类 result:'.json_encode($result));
            }
        } catch (\Exception $exception){
            $this->log('zhifu catch :',$exception);
            CommonReturn::fail(StatusCode::COMMON_UNKNOWN,$exception->getMessage());
        }
        $result['real_time_status'] = $real_time_status;
        $this->log('result : real_time_status:'.$real_time_status);
        return $result;
    }

    /**
     * 修改订单为支付状态
     * @param $app_id
     * @param $out_trade_no
     * @param $total_fee
     * @param $pay_time int 支付时间
     * @param $transaction_id
     * @return PaymentOrderModel|\Illuminate\Database\Eloquent\Model|object|null
     */
    public function isPay($app_id,$out_trade_no,$total_fee,$pay_time,$transaction_id){
        //获取订单信息
        $pay = PaymentOrderModel::getOrderByOutTradeNo($app_id,$out_trade_no);
        if(empty($pay)) {
            $this->log('isPay 订单不存在！');
            CommonReturn::fail(StatusCode::COMMON_NO_DATA_EXIST,"isPay 订单不存在！");
        }
        $pay->transaction_id = $transaction_id;
        $pay->amount = $total_fee;
        $pay->is_pay = ConstCode::PAY_STATUS_YES;
        $pay->pay_time = $pay_time;
        $pay->pay_date_time = date('Y-m-d H:i:s',$pay_time);
        $pay->save();

        //同步实时支付状态-已支付
        RealTimePayCache::setStatus($pay['out_trade_no'],ConstCode::REAL_TIME_PAY_SUCCESS);
        return $pay;
    }


    /**
     * @param $payment_method
     * @return bool
     */
    public function checkPaymentMethod($payment_method){
        if(!in_array($payment_method,[
            ConstCode::PAYMENT_ALIPAY,
            ConstCode::PAYMENT_WECHAT,
        ])) {
            CommonReturn::fail(StatusCode::COMMON_PARAM_INVALID);
        }
        return true;
    }

    /**
     * @param $msg
     * @param $data
     * @return void
     */
    public function log($msg,$data=[]){
        $str = '';
        if(!empty($this->params['order_sn'])){
            $str .= 'order_sn '.$this->params['order_sn'].' : ';
        }
        if(!empty($this->params['out_trade_no'])){
            $str .= 'out_trade_no '.$this->params['out_trade_no'].' : ';
        }
        $str .= $msg;
        if(!empty($data)){
            if($data instanceof \Exception){
                $exceptionArray = [
                    'file'=>$data->getFile(),
                    'line'=>$data->getLine(),
                    'message'=>$data->getMessage()
                ];
                $str .= implode(',',$exceptionArray);
            }elseif(is_array($data)){
                $str .= json_encode($data);
            }else{
                $str .= $data;
            }
        }
        Log::channel('paylog')->info($str);
    }

    /**
     * @return never
     */
    public function error(){
        return abort(400, 'error');
    }

    /**
     * @return string
     */
    public function success()
    {
        return 'success';
    }

}
